/*
 * ManagedSet.java
 *
 * Created on November 6, 2006, 10:32 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package edu.umn.cs5115.scheduler.framework;

import edu.umn.cs5115.scheduler.framework.KeyValueObserver.SetMutationType;

import java.lang.IllegalStateException;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;

/**
 * A set that is used as an attribute to a ManagedData object.  You can use it
 * like a normal set, but whenever you make changes, it will register for undo
 * and send notifications.
 * @author grant
 */
public class ManagedSet extends KeyValueCodingSet
{
    /** Our document. */
    private Document document;
    
    /** 
     * Creates a new instance of ManagedSet.  A HashSet is used to implement
     * set operations.
     * @param document The document the set is in.
     */
    public ManagedSet(Document document)
    {
        this(document, new HashSet());
    }
    
    /**
     * Create a new instance of ManagedSet using another set.  This set is used
     * to implement underlying HashSet operations, so it is important that the
     * set not be modified after being used to construct the ManagedSet.
     * @param document The document the set is in.
     * @param set A set to use to perform set operations.  The set is used directly
     * without copying.  Do not modify set after using it to construct a
     * ManagedSet or use it to create other ManagedSets.
     */
    public ManagedSet(Document document, Set set)
    {
        super(set);
        this.document = document;
    }
    
    /**
     * Called before the KeyValueCodingSet modifies itself.  We will use this
     * notification to register for undo.
     * @param key The key that changed.  The only one we're interested in is
     * SET_CHANGE_NOTIFICATION_KEY.
     * @param mutationType The type of the mutation performed on the set.
     * @param modifyingSet The set of objects used to perform the modification.
     */
    protected void willChangeValueForKey(String key, KeyValueObserver.SetMutationType mutationType, Set modifyingSet)
    {
        if (key == SET_CHANGE_NOTIFICATION_KEY)
        {
            switch (mutationType)
            {
                case INTERSECT:
                    // intersect...objects will be removed.  Find out which ones,
                    // and save a copy of them
                    {
                        final Set objectsToReplace = new HashSet(this);
                        objectsToReplace.removeAll(modifyingSet);
                        getDocument().getUndoManager().registerUndo(new Undoable()
                        {
                            public void undo()
                            {
                                addAll(objectsToReplace);
                            }
                        });
                        break;
                    }
                case MINUS:
                    // minus...objects will be removed.  Find out which of the
                    // set will actually be removed, and save a copy of those.
                    {
                        final Set objectsToReplace = new HashSet(modifyingSet);
                        objectsToReplace.retainAll(this);
                        getDocument().getUndoManager().registerUndo( new Undoable()
                        {
                            public void undo()
                            {
                                addAll(objectsToReplace);
                            }
                        });
                        break;
                    }
                case UNION:
                    // objects will be added.  Find out which ones aren't already
                    // in the set and save a copy to later remove them.
                    {
                        final Set objectsToRemove = new HashSet(modifyingSet);
                        modifyingSet.removeAll(this);
                        getDocument().getUndoManager().registerUndo(new Undoable()
                        {
                            public void undo()
                            {
                                removeAll(objectsToRemove);
                            }
                        });
                    }
                    break;
                case REPLACE:
                    // Set will be replaced entirely.  Save a copy of the current
                    // set.
                    {
                        final Set currentSet = new HashSet(this);
                        getDocument().getUndoManager().registerUndo(new Undoable()
                        {
                            public void undo()
                            {
                            }
                        });
                    }
                    break;
            }
        }
    }
    
    /**
     * Gets the document that this ManagedSet is in. 
     * @return The document of this ManagedSet
     */
    public Document getDocument()
    {
        return document;
    }

}
    
